<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Think Distributed Systems - 视频学习助手</title>
    <!-- React & ReactDOM -->
    <script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
    <!-- Babel for JSX -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Phosphor Icons -->
    <script src="https://unpkg.com/@phosphor-icons/web"></script>
    <style>
        /* Custom scrollbar for better aesthetics */
        ::-webkit-scrollbar {
            width: 8px;
        }
        ::-webkit-scrollbar-track {
            background: #f1f1f1;
        }
        ::-webkit-scrollbar-thumb {
            background: #cbd5e1;
            border-radius: 4px;
        }
        ::-webkit-scrollbar-thumb:hover {
            background: #94a3b8;
        }
        .active-tab {
            border-bottom: 2px solid #2563eb;
            color: #2563eb;
            font-weight: 600;
        }
        .transcript-item:hover {
            background-color: #f8fafc;
        }
    </style>
</head>
<body class="bg-gray-50 h-screen overflow-hidden text-slate-800">

    <div id="root" class="h-full"></div>

    <script type="text/babel">
        const { useState, useEffect, useRef } = React;

        // --- Data: Transcript, Summary, Resources ---

        const VIDEO_ID = "FBKDHpkdrGk";

        const summaryData = [
            {
                title: "视频简介",
                content: "本期 'Flying High with Flutter' 节目邀请到了《Think Distributed Systems》一书的作者 Dominik Tornow。 Dominik 在节目中深入浅出地探讨了分布式系统的核心概念，不仅仅是针对后端工程师，对于所有现代软件开发者（包括移动端）都至关重要。"
            },
            {
                title: "核心类比：办公楼模型 (The Office Building Analogy)",
                content: "Dominik 提出了一个非常形象的 '办公楼' 类比来解释分布式系统：\n1. 每个进程就像办公楼里的一个房间，房间里只有一个人。\n2. 人与人之间不能直接说话（没有共享内存），只能通过 '气动管' (Pneumatic Tubes) 互相发送信件（消息传递）。\n3. 这个模型完美对应了分布式系统的两个核心挑战：并发 (Concurrency) 和 分布 (Distribution/Partial Failure)。"
            },
            {
                title: "CAP 定理的误区与真相",
                content: "Dominik 对 CAP 定理提出了批评。他区分了 'CAP 猜想' (实用但非定理) 和 'CAP 定理' (形式化证明但不实用)。他指出 CAP 定理中的 '可用性' 要求所有节点在任何时候都必须响应，这在现实中是不切实际的。相比之下，分布式共识算法（如 Raft, Paxos）通过 Quorum (法定人数) 机制，使得系统即使在部分节点故障时也能保持一致性和可用性，这实际上比 CAP 定理描述的更具指导意义。"
            },
            {
                title: "Safety (安全性) vs Liveness (活性)",
                content: "引用 Leslie Lamport 的理论：\n- **Safety**: 保证 '坏事永远不会发生' (Something bad never happens)。例如：银行转账不能凭空消失钱。\n- **Liveness**: 保证 '好事最终会发生' (Something good eventually happens)。例如：转账最终会到账。\n在分布式系统中，我们经常需要在故障发生时，在 Safety 和 Liveness 之间做权衡。"
            },
            {
                title: "消息传递与幂等性 (Idempotency)",
                content: "在不可靠的网络中（如移动端），我们无法实现 '恰好一次' (Exactly-once) 的消息传递。我们通常能做到的是 '至少一次' (At-least-once) 加上 **幂等性** (Idempotency)。\n- **策略**：客户端生成一个唯一的 ID (Idempotency Key)，服务器收到消息后记录这个 ID。如果客户端重试发送相同的消息，服务器看到 ID 已存在，就不再重复处理，从而实现逻辑上的 '恰好一次'。"
            }
        ];

        const resourcesData = [
            {
                title: "书籍推荐",
                items: [
                    { label: "Think Distributed Systems (Manning Publications)", link: "https://www.manning.com/books/think-distributed-systems", desc: "Dominik Tornow 所著，通过生动的插图和心智模型来解释分布式系统。" }
                ]
            },
            {
                title: "核心概念",
                items: [
                    { label: "CAP 定理", desc: "Consistency (一致性), Availability (可用性), Partition Tolerance (分区容错性)。分布式系统中的经典权衡理论。" },
                    { label: "TLA+ (Leslie Lamport)", desc: "一种用于建模并发和分布式系统的形式化规范语言。Dominik 提到的 Safety 和 Liveness 概念深受其影响。" },
                    { label: "Actor 模型", desc: "一种并发计算模型，强调通过消息传递进行通信，Erlang, Elixir 和 Dart (Flutter) 都受此影响。" }
                ]
            },
            {
                title: "人物",
                items: [
                    { label: "Dominik Tornow", link: "https://twitter.com/DominikTornow", desc: "分布式系统专家，作者，Resonate HQ 创始人。" }
                ]
            }
        ];

        // Processed transcript with manual translation mapping
        // Due to length, I've segmented and translated the key parts provided.
        const transcriptData = [
            { time: "00:00:10", en: "Hello, welcome to another episode of Flying High with Flutter. I'm your host Alan Wima.", zh: "你好，欢迎收看新一期的《Flying High with Flutter》，我是主持人 Alan Wima。" },
            { time: "00:00:16", en: "Today we have a very interesting, amazing guest Dominik Tornow.", zh: "今天我们要请到一位非常有趣、了不起的嘉宾 Dominik Tornow。" },
            { time: "00:00:23", en: "He wrote a very interesting book that resonates a lot with me called Think Distributed Systems.", zh: "他写了一本非常有趣的书，让我产生了很多共鸣，书名叫做《Think Distributed Systems》（分布式系统思考）。" },
            { time: "00:00:39", en: "People are actually working with distributed systems more than they think.", zh: "人们实际使用分布式系统的情况比他们想象的要多。" },
            { time: "00:00:48", en: "If you have an app, an API server, and a database, you already have a distributed system.", zh: "如果你有一个 App，一个 API 服务器和一个数据库，你就已经拥有一个分布式系统了。" },
            { time: "00:00:58", en: "All modern systems today are distributed systems. It's only a question to what degree.", zh: "当今所有的现代系统都是分布式系统。问题只在于分布式的程度如何。" },
            { time: "00:01:40", en: "I came to the United States for an internship in 2006... that was the year the cloud was born.", zh: "我于 2006 年来到美国实习……那一年正是云计算诞生的一年。" },
            { time: "00:02:25", en: "AWS released S3 and EC2... basically the birth year of the cloud.", zh: "AWS 发布了 S3 和 EC2……基本上那是云计算的诞生之年。" },
            { time: "00:03:04", en: "I was fascinated by the problems that were presented by distributed systems.", zh: "我被分布式系统所带来的问题深深吸引了。" },
            { time: "00:04:54", en: "I like to break large concepts down into fundamental building blocks, thinking from first principles.", zh: "我喜欢将大概念分解为基本的构建块，从第一性原理进行思考。" },
            { time: "00:05:22", en: "There are two forces at play: Concurrency and Distribution.", zh: "这里有两种力量在起作用：并发 (Concurrency) 和 分布 (Distribution)。" },
            { time: "00:05:35", en: "Concurrency is defined by nondeterministic partial order. You don't know what happens next.", zh: "并发的定义是不确定的偏序。你不知道接下来会发生什么。" },
            { time: "00:05:48", en: "Distribution is defined by nondeterministic partial failure. You don't know what fails next.", zh: "分布的定义是不确定的部分故障。你不知道接下来什么会坏掉。" },
            { time: "00:06:30", en: "I settled on the office building analogy. Every room is occupied by one person.", zh: "我选定了办公楼这个类比。每个房间里只有一个人。" },
            { time: "00:06:42", en: "That person cannot communicate by yelling, they have to send letters via pneumatic tubes.", zh: "那个人不能通过喊话交流，他们必须通过气动管发送信件。" },
            { time: "00:07:39", en: "A distributed system is a collection of concurrent components with exclusive access to their own state.", zh: "分布式系统是一组并发组件的集合，它们对自己拥有的状态享有独占访问权。" },
            { time: "00:07:49", en: "They share no state and communicate by exchanging messages over the network.", zh: "它们不共享状态，而是通过网络交换消息进行通信。" },
            { time: "00:09:55", en: "If we have Bob the mail room attendant who is a little disorganized, sometimes he forgets messages.", zh: "如果我们有一个收发室管理员 Bob，他有点乱，有时会忘记发送消息。" },
            { time: "00:10:35", en: "That's what we call an asynchronous system model in the presence of failure.", zh: "这就是我们所说的在存在故障情况下的异步系统模型。" },
            { time: "00:13:08", en: "Shift in perspective: In the office, there is only you and the rest of the world.", zh: "视角的转变：在办公室里，只有你和“世界的其余部分”。" },
            { time: "00:14:25", en: "We often look at distributed systems from a God-like perspective, but no single component has that view.", zh: "我们通常以上帝视角看待分布式系统，但没有任何单一组件拥有那种视角。" },
            { time: "00:17:09", en: "CAP Theorem. I have very strong feelings about the CAP theorem.", zh: "CAP 定理。我对 CAP 定理有非常强烈的看法。" },
            { time: "00:17:55", en: "There is the CAP Conjecture (tangible but not a theorem) and the CAP Theorem (proven but impractical).", zh: "有 CAP 猜想（具体但不算定理）和 CAP 定理（已证明但不切实际）。" },
            { time: "00:18:34", en: "CAP is often misunderstood as 'two out of three'. Partitioning is not a choice, it's a reality.", zh: "CAP 常被误解为“三选二”。分区 (Partitioning) 不是一种选择，它是现实常态。" },
            { time: "00:20:09", en: "In the CAP Theorem, Consistency is defined as linearizability.", zh: "在 CAP 定理中，一致性 (Consistency) 被定义为线性一致性。" },
            { time: "00:21:25", en: "The requirement that ANY server must be able to respond is impractical.", zh: "要求“任何”服务器都必须能够响应，这是不切实际的。" },
            { time: "00:24:06", en: "Distributed consensus (like Paxos/Raft) allows us to make progress as long as a quorum exists.", zh: "分布式共识（如 Paxos/Raft）允许我们只要存在法定人数 (Quorum) 就能继续运行。" },
            { time: "00:24:37", en: "That is the true breakthrough, not CAP.", zh: "这才是真正的突破，而不是 CAP。" },
            { time: "00:28:44", en: "Any property of a system is a combination of Safety properties and Liveness properties.", zh: "系统的任何属性都是安全性 (Safety) 属性和活性 (Liveness) 属性的结合。" },
            { time: "00:28:52", en: "Safety: Nothing bad is ever going to happen.", zh: "安全性：坏事永远不会发生。" },
            { time: "00:29:00", en: "Liveness: Something good is eventually going to happen.", zh: "活性：好事最终会发生。" },
            { time: "00:33:03", en: "Message delivery: At most once, at least once, and exactly once.", zh: "消息传递：至多一次，至少一次，以及恰好一次。" },
            { time: "00:34:11", en: "Nobody can guarantee exactly-once delivery and processing generally.", zh: "通常没有人能保证“恰好一次”的传递和处理。" },
            { time: "00:36:46", en: "At most once: Either it happened or it didn't, I won't worry about it.", zh: "至多一次：要么发生了要么没发生，我不再操心了。" },
            { time: "00:37:53", en: "At least once: If I don't get a receipt, I send the request again.", zh: "至少一次：如果我没收到回执，我就再次发送请求。" },
            { time: "00:39:14", en: "We combine 'at least once' delivery with Idempotency.", zh: "我们将“至少一次”传递与“幂等性”结合起来。" },
            { time: "00:39:23", en: "I put an invoice number on my letter. If Bob sees it twice, he knows he already paid it.", zh: "我在信上写上发票编号。如果 Bob 看到两次，他就知道他已经付过款了。" },
            { time: "00:43:17", en: "Think in terms of message passing, not TCP connections.", zh: "要用消息传递的思维来思考，而不是 TCP 连接。" },
            { time: "00:45:02", en: "Start with the client adding IDs that can be used as idempotent keys.", zh: "从客户端添加可以用作幂等键 (Idempotent Keys) 的 ID 开始。" },
            { time: "00:49:40", en: "I was booking a flight, the app was glitchy, and I got charged multiple times.", zh: "我在订机票，APP 出故障了，结果我被扣了好几次款。" },
            { time: "00:50:38", en: "Eventually the system reconciled, ensuring safety, but the user experience was terrible.", zh: "最终系统对账了，保证了安全性，但用户体验非常糟糕。" },
            { time: "00:52:41", en: "The concept of a Holarchy (not hierarchy) helps reasoning about complex systems.", zh: "Holarchy（全子与整体，非层级结构）的概念有助于推理复杂的系统。" },
            { time: "00:53:16", en: "A Holon is a whole in and of itself, but also part of something bigger.", zh: "全子 (Holon) 本身是一个整体，同时也是更大整体的一部分。" }
        ];

        // --- Components ---

        function App() {
            const [activeTab, setActiveTab] = useState('transcript');
            const playerRef = useRef(null);

            // Load YouTube API
            useEffect(() => {
                if (!window.YT) {
                    const tag = document.createElement('script');
                    tag.src = "https://www.youtube.com/iframe_api";
                    const firstScriptTag = document.getElementsByTagName('script')[0];
                    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
                    
                    window.onYouTubeIframeAPIReady = () => {
                        playerRef.current = new window.YT.Player('player', {
                            videoId: VIDEO_ID,
                            events: {
                                'onReady': onPlayerReady,
                            }
                        });
                    };
                } else {
                     playerRef.current = new window.YT.Player('player', {
                            videoId: VIDEO_ID,
                            events: {
                                'onReady': onPlayerReady,
                            }
                        });
                }
            }, []);

            const onPlayerReady = (event) => {
                // Player is ready
            };

            const seekTo = (timeStr) => {
                if (playerRef.current && playerRef.current.seekTo) {
                    // Convert "00:01:40" to seconds
                    const parts = timeStr.split(':');
                    const seconds = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]);
                    playerRef.current.seekTo(seconds, true);
                    playerRef.current.playVideo();
                }
            };

            return (
                <div className="flex flex-col md:flex-row h-screen">
                    {/* Left: Video Panel */}
                    <div className="w-full md:w-1/2 bg-black flex items-center justify-center relative">
                        <div id="player" className="w-full h-full absolute inset-0"></div>
                        {/* Fallback/Placeholder if JS loads slowly */}
                        <div className="text-white z-0">Loading Video...</div>
                    </div>

                    {/* Right: Info Panel */}
                    <div className="w-full md:w-1/2 flex flex-col bg-white h-1/2 md:h-full overflow-hidden">
                        {/* Tabs Header */}
                        <div className="flex border-b border-slate-200 bg-white">
                            <button 
                                onClick={() => setActiveTab('transcript')}
                                className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'transcript' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`}
                            >
                                全文字幕
                            </button>
                            <button 
                                onClick={() => setActiveTab('summary')}
                                className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'summary' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`}
                            >
                                核心总结
                            </button>
                            <button 
                                onClick={() => setActiveTab('resources')}
                                className={`flex-1 py-4 text-center text-sm font-medium transition-colors ${activeTab === 'resources' ? 'active-tab bg-blue-50' : 'text-slate-500 hover:text-slate-700'}`}
                            >
                                相关资料
                            </button>
                        </div>

                        {/* Tab Content Area */}
                        <div className="flex-1 overflow-y-auto p-6 scroll-smooth">
                            
                            {/* Transcript Tab */}
                            {activeTab === 'transcript' && (
                                <div className="space-y-6">
                                    <div className="text-xs text-slate-400 mb-4 flex items-center gap-1">
                                        <i className="ph ph-info"></i> 点击文字可跳转视频进度
                                    </div>
                                    {transcriptData.map((item, index) => (
                                        <div 
                                            key={index} 
                                            onClick={() => seekTo(item.time)}
                                            className="group cursor-pointer p-3 rounded-lg hover:bg-blue-50 transition-all border border-transparent hover:border-blue-100"
                                        >
                                            <div className="flex items-center gap-2 mb-1">
                                                <span className="text-xs font-mono bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded group-hover:bg-blue-200 group-hover:text-blue-700 transition-colors">
                                                    [{item.time}]
                                                </span>
                                            </div>
                                            <p className="text-base text-slate-800 leading-relaxed font-medium mb-1">
                                                {item.zh}
                                            </p>
                                            <p className="text-sm text-slate-500 leading-relaxed group-hover:text-slate-600">
                                                {item.en}
                                            </p>
                                        </div>
                                    ))}
                                </div>
                            )}

                            {/* Summary Tab */}
                            {activeTab === 'summary' && (
                                <div className="space-y-8 animate-fade-in">
                                    {summaryData.map((section, index) => (
                                        <div key={index} className="bg-white border border-slate-100 rounded-xl p-5 shadow-sm">
                                            <h3 className="text-lg font-bold text-slate-800 mb-3 flex items-center gap-2">
                                                <div className="w-1 h-5 bg-blue-500 rounded-full"></div>
                                                {section.title}
                                            </h3>
                                            <p className="text-slate-600 leading-7 whitespace-pre-line">
                                                {section.content}
                                            </p>
                                        </div>
                                    ))}
                                </div>
                            )}

                            {/* Resources Tab */}
                            {activeTab === 'resources' && (
                                <div className="space-y-8">
                                    {resourcesData.map((category, index) => (
                                        <div key={index}>
                                            <h3 className="text-sm uppercase tracking-wider text-slate-400 font-bold mb-4 border-b border-slate-100 pb-2">
                                                {category.title}
                                            </h3>
                                            <ul className="space-y-4">
                                                {category.items.map((item, idx) => (
                                                    <li key={idx} className="bg-white p-4 rounded-lg border border-slate-200 hover:border-blue-300 transition-colors shadow-sm">
                                                        <div className="flex justify-between items-start">
                                                            <div>
                                                                <h4 className="font-semibold text-slate-800 text-base mb-1">
                                                                    {item.link ? (
                                                                        <a href={item.link} target="_blank" className="hover:text-blue-600 hover:underline flex items-center gap-1">
                                                                            {item.label}
                                                                            <i className="ph ph-arrow-square-out text-xs"></i>
                                                                        </a>
                                                                    ) : (
                                                                        item.label
                                                                    )}
                                                                </h4>
                                                                <p className="text-sm text-slate-500 leading-relaxed mt-1">
                                                                    {item.desc}
                                                                </p>
                                                            </div>
                                                        </div>
                                                    </li>
                                                ))}
                                            </ul>
                                        </div>
                                    ))}
                                </div>
                            )}

                        </div>
                    </div>
                </div>
            );
        }

        const root = ReactDOM.createRoot(document.getElementById('root'));
        root.render(<App />);
    </script>
</body>
</html>